﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Text;
using System.Windows.Forms;
using IBNet;
using IBNet.UtilsDataStructuresAndExtensions;
using System.Collections.ObjectModel;

namespace IBNet.LongTermTrade
{
   public partial class LongTermTradeMainForm : Form, IWrapLongTermTradeMainForm
   {
      private bool TryConstructOrdersAndContractsToBeSubmittedWithClosesFromIB(
            string[] symbolsAndDirections,
            out Collection<OrderAndContract> ordersAndContractsToBeSubmitted)
      {
         if (symbolsAndDirections == null || symbolsAndDirections.Length == 0
             || symbolsAndDirections.Length % 2 != 0)
         {
            textBoxMessages.WriteLine("Error: Number of symbols and directions must be divisible by 2.");
            ordersAndContractsToBeSubmitted = null;
            return false;
         }

         if (!SymbolsAndDirectionsHasOnlyTradeDirectionsLAndS(symbolsAndDirections))
         {
            textBoxMessages.WriteLine("Error: Invalid trade direction. Valid trade directions are 'l' for long and 's' for short.");
            ordersAndContractsToBeSubmitted = null;
            return false;
         }

         int numberOfOrders = symbolsAndDirections.Length / 2;
         ordersAndContractsToBeSubmitted = new Collection<OrderAndContract>();

         // This loop populates ordersAndContractsToBeSubmitted.
         // symbolsAndDirections[i] = _symbol to be traded.
         // symbolsAndDirections[i+1] = "l" or "s" (for long or short).
         // symbolsAndDirections[i+2] = _symbol to be traded.
         // symbolsAndDirections[i+3] = "l" or "s" (for long or short).
         // symbolsAndDirections[i+4] = _symbol to be traded.
         // symbolsAndDirections[i+...] = ...
         for (int i = 0; i < symbolsAndDirections.Length; i += 2)
         {
            string stockSymbol = symbolsAndDirections[i].ToUpper();
            string tradeDirection = symbolsAndDirections[i + 1].ToUpper();
            Stock stock = new Stock(stockSymbol);
            Order openingOrder;
            if (tradeDirection == "L")
            {
               openingOrder = NewOpeningOrder(stock, ActionSide.Buy);
            }
            else
            // tradeDirection == "S"
            {
               openingOrder = NewOpeningOrder(stock, ActionSide.Sell);
            }

            if (openingOrder != null)
            {
               ordersAndContractsToBeSubmitted.Add(new OrderAndContract(openingOrder, stock));
            }
            else
            // contract.Symbol is not a valid symbol
            {
               textBoxMessages.WriteLine(stock.Symbol, " is an invalid symbol.");
               return false;
            }
         }
         return true;
      }


      private bool SymbolsAndDirectionsHasOnlyTradeDirectionsLAndS(
         string[] symbolsAndDirections)
      {
         for (int i = 0; i < symbolsAndDirections.Length; i += 2)
         {
            string tradeDirection = symbolsAndDirections[i + 1].ToUpper();
            if (tradeDirection != "L" && tradeDirection != "S")
            {
               return false;
            }
         }
         return true;
      }

      private Order NewOpeningOrder(Contract contract, ActionSide actionSide)
      {
         decimal yesterdayClosingPrice;
         bool didSuccessfullyObtainClosingPrice = TryRequestClosingPrice(
            contract, out yesterdayClosingPrice);

         if (didSuccessfullyObtainClosingPrice)
         {
            decimal limitPrice = CalculateOpeningOrderLimitPrice(
               yesterdayClosingPrice, actionSide);
            //int quantity = 1;
            int quantity = UtilFunction.CalculateQuantityToTrade(
               DesiredPositionSizeInDollars, limitPrice);
            LimitOrder openingOrder = new LimitOrder(actionSide, quantity, limitPrice);
            return openingOrder;
         }
         else
         // contract.Symbol is not a valid symbol
         {
            return null;
         }
      }

      private decimal CalculateOpeningOrderLimitPrice(
         decimal yesterdayClosingPrice, ActionSide actionSide)
      {
         decimal openingOrderLimitPrice = 0;
         switch (actionSide)
         {
         case ActionSide.Buy:
         {
            openingOrderLimitPrice = Math.Round(
               LongOpeningOrderLimitPricePercentage * yesterdayClosingPrice, 2);
            break;
         }
         case ActionSide.Sell:
         {
            openingOrderLimitPrice = Math.Round(
               ShortOpeningOrderLimitPricePercentage * yesterdayClosingPrice, 2);
            break;
         }
         }
         return openingOrderLimitPrice;
      }

      private bool TryRequestClosingPrice(Contract contract,
         out decimal closingPrice)
      {
         int marketDataRequestId = ClientWrappers.RequestMarketData(contract);

         // Wait for closing price to be received from IB
         // or wait for Error 200 Symbol Not Found error.
         _closingPriceReceivedOrSymbolNotFound.WaitOne();

         // See client_PriceTickReceived().
         if (_symbolFoundWhenRequestingClosingPrice)
         {
            closingPrice = _marketDataRequestIdToClosingPrice[marketDataRequestId];
            _symbolFoundWhenRequestingClosingPrice = false;
            return true;
         }
         else
         {
            closingPrice = 0;
            return false;
         }
      }

      private void SubmitOrders(IEnumerable<OrderAndContract> ordersAndContracts)
      {
         foreach (OrderAndContract orderAndContract in ordersAndContracts)
         {
            ClientWrappers.SubmitOrder(orderAndContract.Order, orderAndContract.Contract,
               OrderFlavor.Opening);
         }
      }

      // Prints "Done processing order: msft l goog s yhoo s " 
      // to textBoxMessages for order "msft l goog s yhoo s".
      private void PrintOrderCompletionMessage(string[] orderString)
      {
         StringBuilder orderCompletionMessage = new StringBuilder(
            "Done processing order: ");
         foreach (string symbolDirectionOrClose in orderString)
         {
            orderCompletionMessage.Append(symbolDirectionOrClose);
            orderCompletionMessage.Append(" ");
         }
         textBoxMessages.WriteLine(orderCompletionMessage.ToString());
      }
   }
}
